
#include <g-bios.h>
#include <flash/flash.h>
#include <sysconf.h>
#include <core/fs.h>
#include <autoconf.h>
#include <sysconf.h>
#include <string.h>
#include <flash/part.h>
#include <image.h>


static int g_nCurPartIndex = DEF_VOL_ID;

static const PART_TYPE g_FreezedPartType[] = {
	PT_BL_GBIOS,
};


static BOOL PartOprPerm(PART_TYPE enPartType)
{
	int i;

	for (i = 0; i < ARRAY_ELEM_NUM(g_FreezedPartType); i++)
	{
		if (g_FreezedPartType[i] == enPartType)
			return FALSE;
	}

	return TRUE;
}

static int FlashGetParts(const struct Flash *pFlash)
{
	return pFlash->pPartInfo->nParts;
}

static struct Partition *FlashGetPart(struct Flash *pFlash, int nPartNum)
{
	return pFlash->vPartTab + nPartNum;
}


static struct Partition *GetPart(int nPartIndex)
{
	int nBasePart = 0, nParts;
	int nFlashIndex;
	struct Flash     *pFlash;
	struct Partition *pPart;

	BUG_IF (nPartIndex < 0);

	for (nFlashIndex = 0; pFlash = GkFlashGet(nFlashIndex); nFlashIndex++)
	{
		nParts = FlashGetParts(pFlash);

		if (nPartIndex < nBasePart + nParts)
		{
			pPart = FlashGetPart(pFlash, nPartIndex - nBasePart);

			pPart->pHost = pFlash; // fixme: to be removed

			return pPart;
		}

		nBasePart += nParts;
	}

	return NULL;
}


struct Partition *GuPartOpen(int nPartIndex)
{
	struct Flash      *pFlash;
	struct Partition  *pPart;
	struct PartAttr   *pAttr;
	struct BlockBuff  *pBlkBuf;
	struct ImageCache *pImgCache;


	pPart = GetPart(nPartIndex);
	if (NULL == pPart)
		return NULL;

	if (NULL == (pAttr = pPart->pAttr))
		return NULL;

	pFlash = pPart->pHost;

	pBlkBuf = &pPart->mBlkBuf;

	switch (pAttr->nPartType)
	{
	case PT_FS_YAFFS:
	case PT_FS_YAFFS2:
		pBlkBuf->nBuffSize = (pFlash->nWriteSize + pFlash->nOobSize) << (pFlash->nEraseShift - pFlash->nWriteShift);
		break;

	default:
		pBlkBuf->nBuffSize = pFlash->nEraseSize;
		break;
	}

	pBlkBuf->pBuffPtr = pBlkBuf->pBuffBase = malloc(pBlkBuf->nBuffSize);

	pPart->ulCurPos = 0;

	return pPart;
}


struct Partition *GuPartOpenCur(void)
{
	return GuPartOpen(g_nCurPartIndex);
}


int GuPartClose(struct Partition *pPart)
{
	int ret, nRedu;
	ULONG nFlashPos;
	struct PartAttr *pAttr;
	struct BlockBuff  *pBlkBuf;
	struct Flash	  *pFlash;
	UINT32 dwEraseFlag = EDF_ALLOWBB;


	if (NULL == pPart)
		return -EINVAL;

	pFlash = pPart->pHost;
	BUG_IF(NULL == pFlash);

	pAttr = pPart->pAttr;

	pBlkBuf = &pPart->mBlkBuf;

	nRedu = pBlkBuf->pBuffPtr - pBlkBuf->pBuffBase;

	if (pPart->ulCurPos > 0 || nRedu > 0) // fixme: if mode=write
	{
		int nType;
		ULONG ulPartSize, ulPartBase, nPosAdj;

		nType = GuPartGetType(pPart);
		ulPartBase = GuPartGetBase(pPart);
		ulPartSize = GuPartGetSize(pPart);

		switch (nType)
		{
		case PT_FS_YAFFS:
		case PT_FS_YAFFS2:
			nPosAdj = pPart->ulCurPos / (pFlash->nWriteSize + pFlash->nOobSize) * pFlash->nWriteSize;
			nFlashPos = ulPartBase + nPosAdj;
			ret = GuFlashErase(pFlash, nFlashPos, ulPartSize - nPosAdj, dwEraseFlag);
			break;
			
		case PT_BL_GBIOS:
			nFlashPos = ulPartBase + (GBIOS_BH_OFFSET << pFlash->nEraseShift) + pPart->ulCurPos;
			ret = GuFlashErase(pFlash, nFlashPos, nRedu, dwEraseFlag);
			break;

		case PT_FS_JFFS2:
			// dwEraseFlag |= EDF_JFFS2;
		default: // fixme
			nFlashPos = ulPartBase + pPart->ulCurPos;
			ret = GuFlashErase(pFlash, nFlashPos, ulPartSize - pPart->ulCurPos, dwEraseFlag);
			break;
		}

		if (ret < 0)
		{
			DPRINT("%s(), line %d\n", __FUNCTION__, __LINE__);
			goto L1;
		}

		if (nRedu > 0)
		{
			switch (nType)
			{
			case PT_FS_YAFFS:
			case PT_FS_YAFFS2:
				ret = GuFlashWriteRaw(pFlash, pBlkBuf->pBuffBase, nRedu, &nFlashPos);
				break;

			default:
				ret = GuFlashWrite(pFlash, pBlkBuf->pBuffBase, nRedu, &nFlashPos);
				break;
			}

			if (ret < 0)
			{
				DPRINT("%s(), line %d\n", __FUNCTION__, __LINE__);
				goto L1;
			}

			pPart->ulCurPos += nRedu;
		}
	}

L1:
	free(pBlkBuf->pBuffBase);

	return ret < 0 ? ret : 0;
}

#if 0
long GuPartRead(struct Partition *pPart, void *pBuff, ULONG nSize)
{
	printf("Flash read error!\n");

	return -EIO;
}
#endif

long GuPartWrite(struct Partition *pPart, const void *pBuff, ULONG nSize)
{
	int ret;
	ULONG nBuffSpace, nFlashPos;
	ULONG ulPartBase;
	UINT32 dwEraseFlag = EDF_ALLOWBB;
	PART_TYPE nType;
	struct Flash *pFlash;
	struct ImageCache *pImgCache;
	struct PartAttr   *pAttr;
	struct BlockBuff  *pBlkBuf;

	if (NULL == pPart || NULL == pBuff)
		return -EINVAL;

	if (0 == nSize)
		return 0;
	
	pFlash = pPart->pHost;
	BUG_IF(NULL == pFlash);

	nType = GuPartGetType(pPart);
	ulPartBase = GuPartGetBase(pPart);

	pImgCache = pPart->pImgCache;
	
	pAttr = pPart->pAttr;

	if (nSize + pPart->ulCurPos > pAttr->ulPartSize)
	{
		char szTmp[32];

		GuValToHrStr(pAttr->ulPartSize, szTmp);
		printf("File size too large! (> %s)\n", szTmp);

		return -ENOMEM;
	}

	pBlkBuf    = &pPart->mBlkBuf;
	nBuffSpace = pBlkBuf->nBuffSize - (pBlkBuf->pBuffPtr - pBlkBuf->pBuffBase);

	while (nSize > 0)
	{
		if (nSize >= nBuffSpace)
		{
			UINT32 nLenAdj;

			memcpy(pBlkBuf->pBuffPtr, pBuff, nBuffSpace);

			nSize -= nBuffSpace;
			pBuff  = (BYTE *)pBuff + nBuffSpace;

			nBuffSpace = pBlkBuf->nBuffSize;

			if (pPart->ulCurPos < pBlkBuf->nBuffSize)
			{
				if (FALSE == GuImageCheck(nType, pBlkBuf->pBuffBase))
				{
					printf("Image type mismatch! Please check the image name and the target partition!\n");
					return -EINVAL;
				}
			}

			nLenAdj = pBlkBuf->nBuffSize;

			switch (nType)
			{
			case PT_FS_YAFFS:
			case PT_FS_YAFFS2:
				nLenAdj   = nLenAdj / (pFlash->nWriteSize + pFlash->nOobSize) << pFlash->nWriteShift;
				nFlashPos = ulPartBase + (pPart->ulCurPos / (pFlash->nWriteSize + pFlash->nOobSize) << pFlash->nWriteShift);
				break;

			case PT_BL_GBIOS:
				nFlashPos = ulPartBase + (GBIOS_BH_OFFSET << pFlash->nEraseShift) + pPart->ulCurPos;
				break;

			case PT_FS_JFFS2:
				// dwEraseFlag |= EDF_JFFS2;
			default:
				nFlashPos = ulPartBase + pPart->ulCurPos;
				break;
			}

			ret = GuFlashErase(pFlash, nFlashPos, nLenAdj, dwEraseFlag);
			if (ret < 0)
			{
				DPRINT("%s(), line %d\n", __FUNCTION__, __LINE__);
				goto L1;
			}

			switch (nType)
			{
			case PT_FS_YAFFS:
			case PT_FS_YAFFS2:
				ret = GuFlashWriteRaw(pFlash, pBlkBuf->pBuffBase, pBlkBuf->nBuffSize, &nFlashPos);
				break;
	
			default:
				ret = GuFlashWrite(pFlash, pBlkBuf->pBuffBase, pBlkBuf->nBuffSize, &nFlashPos);
				break;
			}

			if (ret < 0)
			{
				DPRINT("%s(), line %d\n", __FUNCTION__, __LINE__);
				goto L1;
			}

			pPart->ulCurPos  += pBlkBuf->nBuffSize;
			pBlkBuf->pBuffPtr = pBlkBuf->pBuffBase;
		}
		else // fixme: symi write
		{
			memcpy(pBlkBuf->pBuffPtr, pBuff, nSize);

			pBlkBuf->pBuffPtr += nSize;

			nSize = 0;
		}
	}

L1:
	return ret;
}


int GuPartGetIndex(const struct Partition *pPart)
{
	struct Flash *pFlash;

	pFlash = pPart->pHost;

	BUG_IF(NULL == pFlash);
	
	return pPart - pFlash->vPartTab;
}


static struct PartAttr *PartGetAttr(struct PartInfo *pPartInfo, int nFreeIndex)
{
	if (nFreeIndex > pPartInfo->nParts)
	{
		return NULL;
	}

	return pPartInfo->vAttrTab + nFreeIndex;
}


int GuPartCreate(struct PartInfo *pPartInfo, int nFreeIndex, ULONG nSize, PART_TYPE nPartType)
{
	struct PartAttr *pFreeAttr;


	pFreeAttr = PartGetAttr(pPartInfo, nFreeIndex);
	if (NULL == pFreeAttr)
	{
		return -ENODEV;
	}

	// (nSize & (pHost->nEraseSize - 1)) || // fixme
	if (0 == nSize || nSize > pFreeAttr->ulPartSize)
	{
		return -EINVAL;
	}

	if (!PartOprPerm(nPartType))  // reasonable ?
		return -EPERM;

	if (PT_FREE == nPartType || PT_FREE != pFreeAttr->nPartType)
	{
		return -EINVAL;
	}

	// move to AdjustXXX()?
	if (pPartInfo->nParts == MAX_FLASH_PARTS && nSize != pFreeAttr->ulPartSize)
		nSize = pFreeAttr->ulPartSize;

	if (nSize < pFreeAttr->ulPartSize)
	{
		struct PartAttr *pCurrAttr; 

		pCurrAttr = pPartInfo->vAttrTab + pPartInfo->nParts;

		while (pCurrAttr > pFreeAttr)
		{
			*pCurrAttr = *(pCurrAttr - 1);

			pCurrAttr--;
		}

		pFreeAttr[1].ulPartBase += nSize;
		pFreeAttr[1].ulPartSize -= nSize;

		pFreeAttr->ulPartSize = nSize;

		pPartInfo->nParts++;
	}

	pFreeAttr->nPartType = nPartType;

	return 0;
}


int GuPartChange(int nPartIndex)
{
	if (NULL == GetPart(nPartIndex))
		return -ENODEV;

	g_nCurPartIndex = nPartIndex;

	return g_nCurPartIndex;
}


// fixme
int GuPartTabRead(const struct Flash *pHost, struct PartAttr pPartAttr[], int nPartNum)
{
	struct SysConf *pSysConf;
	UINT32 nIndex;


	GuSysConfLoad(&pSysConf);
	if (NULL == pSysConf)
		return -EINVAL;

	if (NULL == pPartAttr)
		return pSysConf->mPartInfo.nParts;
	
	if (nPartNum > pSysConf->mPartInfo.nParts)
		nPartNum = pSysConf->mPartInfo.nParts;

	for (nIndex = 0; nIndex < nPartNum; nIndex++)
		pPartAttr[nIndex] = pSysConf->mPartInfo.vAttrTab[nIndex];

	// fixme: shall we keep the data consistant with FLASH 
	
	return nPartNum;
}


int GuPartGetImgInfo(const struct Partition *pPart,
						char szImgName[MAX_FILE_NAME_LEN],
						ULONG *pulSize
						)
{
	if (pPart->pAttr->ulImgSize && szImgName)
	{
		strncpy(szImgName, pPart->pAttr->szImgName, MAX_FILE_NAME_LEN);
	}

	if (pulSize)
	{
		*pulSize = pPart->pAttr->ulImgSize;
	}

	return pPart->pAttr->ulImgSize;
}


int GuPartSetImgInfo(struct Partition *pPart,
						char szImgName[MAX_FILE_NAME_LEN],
						ULONG ulSize
						)
{
	strncpy(pPart->pAttr->szImgName, szImgName, MAX_FILE_NAME_LEN);
	pPart->pAttr->ulImgSize = ulSize;

	return pPart->pAttr->ulImgSize;
}


